package fr.babos.graphic.components 
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import fr.babos.core.errors.BabosError;
	import fr.babos.core.masters.MasterSprite;
	
	/**
	 * @author Alexis Bouhet
	 */
	public class SkinableComponent extends MasterSprite 
	{
		/**
		 * La largeur du composant
		 */
		protected var _localWidth				:Number					= 0;
		
		/**
		 * La hauteur du composant
		 */
		protected var _localHeight				:Number					= 0;
		
		/**
		 * La définition du skin
		 */
		protected var _skinDefinition			:Class;
		
		/**
		 * Les assets skinables
		 */
		protected var _skinableAssets			:Array;
		
		/**
		 * Le numéro de la frame du skin pour récupérer les assets
		 */
		protected var _skinFrame				:Number;
		
		
		/**
		 * La largeur en passant par la superclasse
		 */
		public function get superWidth ():Number { return super.width; };
		public function set superWidth (value:Number):void
		{
			super.width = value;
		};
		
		/**
		 * La hauter en passant par la superclasse
		 */
		public function get superHeight ():Number { return super.height; };
		public function set superHeight (value:Number):void
		{
			super.height = value;
		};
		
		/**
		 * La largeur du composant
		 */
		override public function get width ():Number { return _localWidth; }
		override public function set width (value:Number):void
		{
			// Enregistrer la nouvelle largeur
			_localWidth = value;
			
			// Appeler le handler protégé
			resized();
		}
		
		/**
		 * La largeur du composant
		 */
		override public function get height ():Number { return _localHeight; }
		override public function set height (value:Number):void
		{
			// Enregistrer la nouvelle largeur
			_localHeight = value;
			
			// Appeler le handler protégé
			resized();
		}
		
		/**
		 * La définition du skin
		 */
		public function get skinDefinition ():Class  { return _skinDefinition; }
		public function set skinDefinition (value:Class):void 
		{
			// Si le skin est différent
			if (value != _skinDefinition)
			{
				// Si on avait déjà une définition de skin
				if (_skinDefinition != null)
				{
					// On signale qu'il va être détruit
					beforeDestroyedSkin();
				}
				
				// Enregistrer la définition
				_skinDefinition = value;
				
				// Initialiser le skin
				initSkin();
			}
		}
		
		/**
		 * Le numéro de la frame du skin pour récupérer les assets
		 */
		public function get skinFrame ():Number { return _skinFrame; }
		public function set skinFrame (value:Number):void 
		{
			// Enregistrer la frame
			_skinFrame = Math.max(1, value);
			
			// Actualiser le skin
			skinDefinition = _skinDefinition;
		}
		
		
		/**
		 * Le constructeur du composant
		 * @param	pSkinDefinition : La définition du skin.
		 * @param	pSkinFrame : L'image du skin a atteindre pour récupérer les éléments.
		 * @param	pSkinableAssets : Un tableau définissant les éléments du skin à récupérer. Si null ou tableau vide, les éléments seront récupérés automatiquement depuis le skin.
		 */
		public function SkinableComponent (pSkinDefinition:Class = null, pSkinFrame:uint = 1, pSkinableAssets:Array = null)
		{
			// On défini le skin
			setSkin(pSkinDefinition, pSkinFrame, pSkinableAssets);
			
			// Relayer
			super();
		}
		
		/**
		 * Définir la taille du composant
		 */
		public function setSize (pWidth:Number, pHeight:Number):void
		{
			// Enregistrer les nouvelles dimensions
			_localWidth = pWidth;
			_localHeight = pHeight;
			
			// Signaler
			resized();
		}
		
		/**
		 * Définir un skin.
		 * @param	pSkinDefinition : La définition du skin.
		 * @param	pSkinFrame : L'image du skin a atteindre pour récupérer les éléments.
		 * @param	pSkinableAssets : Un tableau définissant les éléments du skin à récupérer. Si null ou tableau vide, les éléments seront récupérés automatiquement depuis le skin.
		 */
		public function setSkin (pSkinDefinition:Class = null, pSkinFrame:uint = 1, pSkinableAssets:Array = null):void
		{
			// Enregistrer la frame
			_skinFrame = pSkinFrame;
			
			// Enregistrer les assets du skin
			_skinableAssets = pSkinableAssets;
			
			// Actualiser la définition de skin
			skinDefinition = pSkinDefinition;
		}
		
		/**
		 * Initialiser le skin par défaut
		 */
		protected function initSkin ():void
		{
			// Le skin
			var skin:DisplayObjectContainer;
			
			// Si la définition du skin existe
			if (_skinDefinition != null)
			{
				// Créer le skin
				skin = new _skinDefinition ();
				
				// Si c'est un MovieClip
				if (skin is MovieClip)
				{
					// On le stop à la bonne frame
					(skin as MovieClip).gotoAndStop(_skinFrame);
				}
			}
			
			// Si on a pas de tableau d'assets
			if (_skinableAssets == null)
			{
				// On le créé
				_skinableAssets = [];
			}
			
			// Si on a un skin
			// Et si on a pas d'assets pour la définition de ce skin
			if (skin != null && _skinableAssets.length == 0)
			{
				// On créé le tableau
				_skinableAssets = [];
				
				// Et on le rempli automatiquement
				for (var i:uint = 0; i < skin.numChildren; i ++)
				{
					// Avec les noms des clips dans le skin
					_skinableAssets.push(skin.getChildAt(i).name);
				}
			}
			
			// Parcourir les éléments du skin
			for each (var skinable:String in _skinableAssets)
			{
				// Enlever un skinable
				if (this[skinable] != null && this[skinable] is DisplayObject)
				{
					// Suppression
					removeChild(this[skinable]);
					
					// Signaler la suppression du skinable
					removedSkinableAsset(this[skinable]);
					
					// Passer le skinable à null
					this[skinable] = null;
				}
				
				// Vérifier si cet élément skinable existe dans le skin
				if (skin != null && skin[skinable] != null && skin[skinable] is DisplayObject)
				{
					// Appliquer l'asset du skin a ce composant
					this[skinable] = skin[skinable];
					
					// Ajouter
					addChild(this[skinable]);
					
					// Signaler l'ajout du skinable
					addedSkinableAsset(this[skinable]);
				}
			}
			
			// Si on a un nouveau skin
			if (skin != null)
			{
				// Signaler que le skin est initialisé
				initialisedSkin();
				
				// Replacer les nouveaux éléments
				resized();
			}
		}
		
		/**
		 * Le composant est redimentionné
		 */
		protected function resized ():void
		{
			
		}
		
		/**
		 * Un élément du skin est ajouté a ce composant
		 * @param	pSkinable : Le skinable en question
		 */
		protected function addedSkinableAsset (pSkinable:DisplayObject):void
		{
			
		}
		
		/**
		 * Un élément du skin est supprimé de ce composant
		 * @param	pSkinable : Le skinable en question
		 */
		protected function removedSkinableAsset (pSkinable:DisplayObject):void
		{
			
		}
		
		/**
		 * Le nouveau skin est initialisé.
		 */
		protected function initialisedSkin ():void
		{
			
		}
		
		/**
		 * Le skin actuel va être détruit.
		 */
		protected function beforeDestroyedSkin ():void
		{
			
		}
	}
}